mobbdev 0.0.84 → 0.0.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +491 -112
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -173,7 +173,6 @@ var CliError = class extends Error {
|
|
|
173
173
|
};
|
|
174
174
|
|
|
175
175
|
// src/features/analysis/index.ts
|
|
176
|
-
import { Octokit as Octokit3 } from "@octokit/core";
|
|
177
176
|
import chalk4 from "chalk";
|
|
178
177
|
import Configstore from "configstore";
|
|
179
178
|
import Debug10 from "debug";
|
|
@@ -476,6 +475,17 @@ var GET_FIX = gql2`
|
|
|
476
475
|
}
|
|
477
476
|
}
|
|
478
477
|
`;
|
|
478
|
+
var GET_FIXES = gql2`
|
|
479
|
+
query getFixes($filters: fix_bool_exp!) {
|
|
480
|
+
fixes: fix(where: $filters) {
|
|
481
|
+
issueType
|
|
482
|
+
id
|
|
483
|
+
patchAndQuestions {
|
|
484
|
+
patch
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
`;
|
|
479
489
|
var GET_VUL_BY_NODES_METADATA = gql2`
|
|
480
490
|
query getVulByNodesMetadata(
|
|
481
491
|
$filters: [vulnerability_report_issue_code_node_bool_exp!]
|
|
@@ -486,6 +496,7 @@ var GET_VUL_BY_NODES_METADATA = gql2`
|
|
|
486
496
|
where: {
|
|
487
497
|
_or: $filters
|
|
488
498
|
vulnerabilityReportIssue: {
|
|
499
|
+
fixId: { _is_null: false }
|
|
489
500
|
vulnerabilityReportId: { _eq: $vulnerabilityReportId }
|
|
490
501
|
}
|
|
491
502
|
}
|
|
@@ -498,6 +509,35 @@ var GET_VUL_BY_NODES_METADATA = gql2`
|
|
|
498
509
|
fixId
|
|
499
510
|
}
|
|
500
511
|
}
|
|
512
|
+
fixablePrVuls: vulnerability_report_issue_aggregate(
|
|
513
|
+
where: {
|
|
514
|
+
fixId: { _is_null: false }
|
|
515
|
+
vulnerabilityReportId: { _eq: $vulnerabilityReportId }
|
|
516
|
+
codeNodes: { _or: $filters }
|
|
517
|
+
}
|
|
518
|
+
) {
|
|
519
|
+
aggregate {
|
|
520
|
+
count
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
nonFixablePrVuls: vulnerability_report_issue_aggregate(
|
|
524
|
+
where: {
|
|
525
|
+
fixId: { _is_null: true }
|
|
526
|
+
vulnerabilityReportId: { _eq: $vulnerabilityReportId }
|
|
527
|
+
codeNodes: { _or: $filters }
|
|
528
|
+
}
|
|
529
|
+
) {
|
|
530
|
+
aggregate {
|
|
531
|
+
count
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
totalScanVulnerabilities: vulnerability_report_issue_aggregate(
|
|
535
|
+
where: { vulnerabilityReportId: { _eq: $vulnerabilityReportId } }
|
|
536
|
+
) {
|
|
537
|
+
aggregate {
|
|
538
|
+
count
|
|
539
|
+
}
|
|
540
|
+
}
|
|
501
541
|
}
|
|
502
542
|
`;
|
|
503
543
|
|
|
@@ -711,15 +751,17 @@ var GetAnalysisQueryZ = z2.object({
|
|
|
711
751
|
})
|
|
712
752
|
})
|
|
713
753
|
});
|
|
714
|
-
var
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
patch: z2.string()
|
|
720
|
-
})
|
|
754
|
+
var FixDataZ = z2.object({
|
|
755
|
+
issueType: z2.string(),
|
|
756
|
+
id: z2.string(),
|
|
757
|
+
patchAndQuestions: z2.object({
|
|
758
|
+
patch: z2.string()
|
|
721
759
|
})
|
|
722
760
|
});
|
|
761
|
+
var GetFixQueryZ = z2.object({
|
|
762
|
+
fix_by_pk: FixDataZ
|
|
763
|
+
});
|
|
764
|
+
var GetFixesQueryZ = z2.object({ fixes: z2.array(FixDataZ) });
|
|
723
765
|
var VulnerabilityReportIssueCodeNodeZ = z2.object({
|
|
724
766
|
vulnerabilityReportIssueId: z2.string(),
|
|
725
767
|
path: z2.string(),
|
|
@@ -729,7 +771,22 @@ var VulnerabilityReportIssueCodeNodeZ = z2.object({
|
|
|
729
771
|
})
|
|
730
772
|
});
|
|
731
773
|
var GetVulByNodesMetadataZ = z2.object({
|
|
732
|
-
vulnerabilityReportIssueCodeNodes: z2.array(VulnerabilityReportIssueCodeNodeZ)
|
|
774
|
+
vulnerabilityReportIssueCodeNodes: z2.array(VulnerabilityReportIssueCodeNodeZ),
|
|
775
|
+
nonFixablePrVuls: z2.object({
|
|
776
|
+
aggregate: z2.object({
|
|
777
|
+
count: z2.number()
|
|
778
|
+
})
|
|
779
|
+
}),
|
|
780
|
+
fixablePrVuls: z2.object({
|
|
781
|
+
aggregate: z2.object({
|
|
782
|
+
count: z2.number()
|
|
783
|
+
})
|
|
784
|
+
}),
|
|
785
|
+
totalScanVulnerabilities: z2.object({
|
|
786
|
+
aggregate: z2.object({
|
|
787
|
+
count: z2.number()
|
|
788
|
+
})
|
|
789
|
+
})
|
|
733
790
|
});
|
|
734
791
|
|
|
735
792
|
// src/features/analysis/graphql/gql.ts
|
|
@@ -844,7 +901,6 @@ var GQLClient = class {
|
|
|
844
901
|
const filters = hunks.map((hunk) => {
|
|
845
902
|
const filter = {
|
|
846
903
|
path: { _eq: hunk.path },
|
|
847
|
-
vulnerabilityReportIssue: { fixId: { _is_null: false } },
|
|
848
904
|
_or: hunk.ranges.map(({ endLine, startLine }) => ({
|
|
849
905
|
startLine: { _gte: startLine, _lte: endLine },
|
|
850
906
|
endLine: { _gte: startLine, _lte: endLine }
|
|
@@ -868,10 +924,20 @@ var GQLClient = class {
|
|
|
868
924
|
[vulnerabilityReportIssueCodeNode.vulnerabilityReportIssueId]: vulnerabilityReportIssueCodeNode
|
|
869
925
|
};
|
|
870
926
|
}, {});
|
|
927
|
+
const nonFixablePrVuls = parsedGetVulByNodesMetadataRes.nonFixablePrVuls.aggregate.count;
|
|
928
|
+
const fixablePrVuls = parsedGetVulByNodesMetadataRes.fixablePrVuls.aggregate.count;
|
|
929
|
+
const totalScanVulnerabilities = parsedGetVulByNodesMetadataRes.totalScanVulnerabilities.aggregate.count;
|
|
930
|
+
const vulnerabilitiesOutsidePr = totalScanVulnerabilities - nonFixablePrVuls - fixablePrVuls;
|
|
931
|
+
const totalPrVulnerabilities = nonFixablePrVuls + fixablePrVuls;
|
|
871
932
|
return {
|
|
872
933
|
vulnerabilityReportIssueCodeNodes: Object.values(
|
|
873
934
|
uniqueVulByNodesMetadata
|
|
874
|
-
)
|
|
935
|
+
),
|
|
936
|
+
nonFixablePrVuls,
|
|
937
|
+
fixablePrVuls,
|
|
938
|
+
totalScanVulnerabilities,
|
|
939
|
+
vulnerabilitiesOutsidePr,
|
|
940
|
+
totalPrVulnerabilities
|
|
875
941
|
};
|
|
876
942
|
}
|
|
877
943
|
async digestVulnerabilityReport({
|
|
@@ -977,9 +1043,19 @@ var GQLClient = class {
|
|
|
977
1043
|
);
|
|
978
1044
|
return GetFixQueryZ.parse(res);
|
|
979
1045
|
}
|
|
1046
|
+
async getFixes(fixIds) {
|
|
1047
|
+
const res = await this._client.request(
|
|
1048
|
+
GET_FIXES,
|
|
1049
|
+
{
|
|
1050
|
+
filters: { id: { _in: fixIds } }
|
|
1051
|
+
}
|
|
1052
|
+
);
|
|
1053
|
+
return GetFixesQueryZ.parse(res);
|
|
1054
|
+
}
|
|
980
1055
|
};
|
|
981
1056
|
|
|
982
1057
|
// src/features/analysis/handle_finished_analysis.ts
|
|
1058
|
+
import { Octokit as Octokit3 } from "@octokit/core";
|
|
983
1059
|
import Debug4 from "debug";
|
|
984
1060
|
import parseDiff from "parse-diff";
|
|
985
1061
|
import { z as z9 } from "zod";
|
|
@@ -1009,7 +1085,7 @@ import { Octokit } from "octokit";
|
|
|
1009
1085
|
import { z as z3 } from "zod";
|
|
1010
1086
|
|
|
1011
1087
|
// src/features/analysis/scm/urlParser.ts
|
|
1012
|
-
function getRepoInfo(pathname, hostname) {
|
|
1088
|
+
function getRepoInfo(pathname, hostname, scmType) {
|
|
1013
1089
|
const hostnameParts = hostname.split(".");
|
|
1014
1090
|
if (hostnameParts.length === 3 && hostnameParts[1] === "visualstudio" && hostnameParts[2] === "com") {
|
|
1015
1091
|
if (pathname.length === 2 && pathname[0] === "_git") {
|
|
@@ -1027,7 +1103,7 @@ function getRepoInfo(pathname, hostname) {
|
|
|
1027
1103
|
};
|
|
1028
1104
|
}
|
|
1029
1105
|
}
|
|
1030
|
-
if (hostname === "dev.azure.com") {
|
|
1106
|
+
if (hostname === "dev.azure.com" || scmType === "Ado" /* Ado */) {
|
|
1031
1107
|
if (pathname.length === 3 && pathname[1] === "_git") {
|
|
1032
1108
|
return {
|
|
1033
1109
|
organization: pathname[0],
|
|
@@ -1043,7 +1119,7 @@ function getRepoInfo(pathname, hostname) {
|
|
|
1043
1119
|
};
|
|
1044
1120
|
}
|
|
1045
1121
|
}
|
|
1046
|
-
if (hostname === "github.com") {
|
|
1122
|
+
if (hostname === "github.com" || scmType === "GitHub" /* GitHub */) {
|
|
1047
1123
|
if (pathname.length === 2) {
|
|
1048
1124
|
return {
|
|
1049
1125
|
organization: pathname[0],
|
|
@@ -1052,7 +1128,7 @@ function getRepoInfo(pathname, hostname) {
|
|
|
1052
1128
|
};
|
|
1053
1129
|
}
|
|
1054
1130
|
}
|
|
1055
|
-
if (hostname === "gitlab.com") {
|
|
1131
|
+
if (hostname === "gitlab.com" || scmType === "GitLab" /* GitLab */) {
|
|
1056
1132
|
if (pathname.length >= 2) {
|
|
1057
1133
|
return {
|
|
1058
1134
|
organization: pathname[0],
|
|
@@ -1064,12 +1140,12 @@ function getRepoInfo(pathname, hostname) {
|
|
|
1064
1140
|
return null;
|
|
1065
1141
|
}
|
|
1066
1142
|
var NAME_REGEX = /[a-z0-9\-_.+]+/i;
|
|
1067
|
-
var parseScmURL = (scmURL) => {
|
|
1143
|
+
var parseScmURL = (scmURL, scmType) => {
|
|
1068
1144
|
try {
|
|
1069
1145
|
const url = new URL(scmURL);
|
|
1070
1146
|
const hostname = url.hostname.toLowerCase();
|
|
1071
1147
|
const projectPath = url.pathname.substring(1).replace(/.git$/i, "");
|
|
1072
|
-
const repo = getRepoInfo(projectPath.split("/"), hostname);
|
|
1148
|
+
const repo = getRepoInfo(projectPath.split("/"), hostname, scmType);
|
|
1073
1149
|
if (!repo)
|
|
1074
1150
|
return null;
|
|
1075
1151
|
const { organization, repoName, projectName } = repo;
|
|
@@ -1089,6 +1165,22 @@ var parseScmURL = (scmURL) => {
|
|
|
1089
1165
|
return null;
|
|
1090
1166
|
}
|
|
1091
1167
|
};
|
|
1168
|
+
var sanityRepoURL = (scmURL) => {
|
|
1169
|
+
try {
|
|
1170
|
+
const url = new URL(scmURL);
|
|
1171
|
+
const projectPath = url.pathname.substring(1).replace(/.git$/i, "");
|
|
1172
|
+
const pathParts = projectPath.split("/");
|
|
1173
|
+
if (pathParts.length < 2)
|
|
1174
|
+
return false;
|
|
1175
|
+
if (pathParts.length > 4)
|
|
1176
|
+
return false;
|
|
1177
|
+
if (pathParts.some((part) => !part.match(NAME_REGEX)))
|
|
1178
|
+
return false;
|
|
1179
|
+
return true;
|
|
1180
|
+
} catch (e) {
|
|
1181
|
+
return null;
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1092
1184
|
|
|
1093
1185
|
// src/features/analysis/scm/github/github.ts
|
|
1094
1186
|
function removeTrailingSlash(str) {
|
|
@@ -1369,7 +1461,7 @@ async function getCommit({
|
|
|
1369
1461
|
}
|
|
1370
1462
|
function parseGithubOwnerAndRepo(gitHubUrl) {
|
|
1371
1463
|
gitHubUrl = removeTrailingSlash(gitHubUrl);
|
|
1372
|
-
const parsingResult = parseScmURL(gitHubUrl);
|
|
1464
|
+
const parsingResult = parseScmURL(gitHubUrl, "GitHub" /* GitHub */);
|
|
1373
1465
|
if (!parsingResult || parsingResult.hostname !== "github.com") {
|
|
1374
1466
|
throw new InvalidUrlPatternError(`invalid github repo Url ${gitHubUrl}`);
|
|
1375
1467
|
}
|
|
@@ -1527,6 +1619,9 @@ var UPDATE_COMMENT_PATH = "PATCH /repos/{owner}/{repo}/pulls/comments/{comment_i
|
|
|
1527
1619
|
var GET_PR_COMMENTS_PATH = "GET /repos/{owner}/{repo}/pulls/{pull_number}/comments";
|
|
1528
1620
|
var GET_PR_COMMENT_PATH = "GET /repos/{owner}/{repo}/pulls/comments/{comment_id}";
|
|
1529
1621
|
var GET_PR = "GET /repos/{owner}/{repo}/pulls/{pull_number}";
|
|
1622
|
+
var POST_GENERAL_PR_COMMENT = "POST /repos/{owner}/{repo}/issues/{issue_number}/comments";
|
|
1623
|
+
var GET_GENERAL_PR_COMMENTS = "GET /repos/{owner}/{repo}/issues/{issue_number}/comments";
|
|
1624
|
+
var DELETE_GENERAL_PR_COMMENT = "DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}";
|
|
1530
1625
|
var CREATE_OR_UPDATE_A_REPOSITORY_SECRET = "PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}";
|
|
1531
1626
|
var GET_A_REPOSITORY_PUBLIC_KEY = "GET /repos/{owner}/{repo}/actions/secrets/public-key";
|
|
1532
1627
|
|
|
@@ -1558,10 +1653,20 @@ function createOrUpdateRepositorySecret(client, params) {
|
|
|
1558
1653
|
function getARepositoryPublicKey(client, params) {
|
|
1559
1654
|
return client.request(GET_A_REPOSITORY_PUBLIC_KEY, params);
|
|
1560
1655
|
}
|
|
1656
|
+
function postGeneralPrComment(client, params) {
|
|
1657
|
+
return client.request(POST_GENERAL_PR_COMMENT, params);
|
|
1658
|
+
}
|
|
1659
|
+
function getGeneralPrComments(client, params) {
|
|
1660
|
+
return client.request(GET_GENERAL_PR_COMMENTS, params);
|
|
1661
|
+
}
|
|
1662
|
+
function deleteGeneralPrComment(client, params) {
|
|
1663
|
+
return client.request(DELETE_GENERAL_PR_COMMENT, params);
|
|
1664
|
+
}
|
|
1561
1665
|
|
|
1562
1666
|
// src/features/analysis/scm/gitlab.ts
|
|
1563
1667
|
import querystring from "node:querystring";
|
|
1564
1668
|
import { Gitlab } from "@gitbeaker/rest";
|
|
1669
|
+
import { ProxyAgent } from "undici";
|
|
1565
1670
|
import { z as z4 } from "zod";
|
|
1566
1671
|
function removeTrailingSlash2(str) {
|
|
1567
1672
|
return str.trim().replace(/\/+$/, "");
|
|
@@ -1572,17 +1677,19 @@ var EnvVariablesZod2 = z4.object({
|
|
|
1572
1677
|
var { GITLAB_API_TOKEN } = EnvVariablesZod2.parse(process.env);
|
|
1573
1678
|
function getGitBeaker(options) {
|
|
1574
1679
|
const token = options?.gitlabAuthToken ?? GITLAB_API_TOKEN ?? "";
|
|
1680
|
+
const url = options.url;
|
|
1681
|
+
const host = url ? new URL(url).origin : "https://gitlab.com";
|
|
1575
1682
|
if (token?.startsWith("glpat-") || token === "") {
|
|
1576
|
-
return new Gitlab({ token });
|
|
1683
|
+
return new Gitlab({ token, host });
|
|
1577
1684
|
}
|
|
1578
|
-
return new Gitlab({ oauthToken: token });
|
|
1685
|
+
return new Gitlab({ oauthToken: token, host });
|
|
1579
1686
|
}
|
|
1580
1687
|
async function gitlabValidateParams({
|
|
1581
1688
|
url,
|
|
1582
1689
|
accessToken
|
|
1583
1690
|
}) {
|
|
1584
1691
|
try {
|
|
1585
|
-
const api2 = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
1692
|
+
const api2 = getGitBeaker({ url, gitlabAuthToken: accessToken });
|
|
1586
1693
|
if (accessToken) {
|
|
1587
1694
|
await api2.Users.showCurrentUser();
|
|
1588
1695
|
}
|
|
@@ -1603,8 +1710,8 @@ async function gitlabValidateParams({
|
|
|
1603
1710
|
throw e;
|
|
1604
1711
|
}
|
|
1605
1712
|
}
|
|
1606
|
-
async function getGitlabUsername(accessToken) {
|
|
1607
|
-
const api2 = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
1713
|
+
async function getGitlabUsername(url, accessToken) {
|
|
1714
|
+
const api2 = getGitBeaker({ url, gitlabAuthToken: accessToken });
|
|
1608
1715
|
const res = await api2.Users.showCurrentUser();
|
|
1609
1716
|
return res.username;
|
|
1610
1717
|
}
|
|
@@ -1615,7 +1722,7 @@ async function getGitlabIsUserCollaborator({
|
|
|
1615
1722
|
}) {
|
|
1616
1723
|
try {
|
|
1617
1724
|
const { projectPath } = parseGitlabOwnerAndRepo(repoUrl);
|
|
1618
|
-
const api2 = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
1725
|
+
const api2 = getGitBeaker({ url: repoUrl, gitlabAuthToken: accessToken });
|
|
1619
1726
|
const res = await api2.Projects.show(projectPath);
|
|
1620
1727
|
const members = await api2.ProjectMembers.all(res.id, {
|
|
1621
1728
|
includeInherited: true
|
|
@@ -1631,7 +1738,7 @@ async function getGitlabMergeRequestStatus({
|
|
|
1631
1738
|
mrNumber
|
|
1632
1739
|
}) {
|
|
1633
1740
|
const { projectPath } = parseGitlabOwnerAndRepo(repoUrl);
|
|
1634
|
-
const api2 = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
1741
|
+
const api2 = getGitBeaker({ url: repoUrl, gitlabAuthToken: accessToken });
|
|
1635
1742
|
const res = await api2.MergeRequests.show(projectPath, mrNumber);
|
|
1636
1743
|
switch (res.state) {
|
|
1637
1744
|
case "merged" /* merged */:
|
|
@@ -1648,7 +1755,7 @@ async function getGitlabIsRemoteBranch({
|
|
|
1648
1755
|
branch
|
|
1649
1756
|
}) {
|
|
1650
1757
|
const { projectPath } = parseGitlabOwnerAndRepo(repoUrl);
|
|
1651
|
-
const api2 = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
1758
|
+
const api2 = getGitBeaker({ url: repoUrl, gitlabAuthToken: accessToken });
|
|
1652
1759
|
try {
|
|
1653
1760
|
const res = await api2.Branches.show(projectPath, branch);
|
|
1654
1761
|
return res.name === branch;
|
|
@@ -1656,8 +1763,8 @@ async function getGitlabIsRemoteBranch({
|
|
|
1656
1763
|
return false;
|
|
1657
1764
|
}
|
|
1658
1765
|
}
|
|
1659
|
-
async function getGitlabRepoList(accessToken) {
|
|
1660
|
-
const api2 = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
1766
|
+
async function getGitlabRepoList(url, accessToken) {
|
|
1767
|
+
const api2 = getGitBeaker({ url, gitlabAuthToken: accessToken });
|
|
1661
1768
|
const res = await api2.Projects.all({
|
|
1662
1769
|
membership: true,
|
|
1663
1770
|
//TODO: a bug in the sorting mechanism of this api call
|
|
@@ -1690,7 +1797,7 @@ async function getGitlabBranchList({
|
|
|
1690
1797
|
repoUrl
|
|
1691
1798
|
}) {
|
|
1692
1799
|
const { projectPath } = parseGitlabOwnerAndRepo(repoUrl);
|
|
1693
|
-
const api2 = getGitBeaker({ gitlabAuthToken: accessToken });
|
|
1800
|
+
const api2 = getGitBeaker({ url: repoUrl, gitlabAuthToken: accessToken });
|
|
1694
1801
|
try {
|
|
1695
1802
|
const res = await api2.Branches.all(projectPath, {
|
|
1696
1803
|
perPage: 100,
|
|
@@ -1705,7 +1812,10 @@ async function getGitlabBranchList({
|
|
|
1705
1812
|
}
|
|
1706
1813
|
async function createMergeRequest(options) {
|
|
1707
1814
|
const { projectPath } = parseGitlabOwnerAndRepo(options.repoUrl);
|
|
1708
|
-
const api2 = getGitBeaker({
|
|
1815
|
+
const api2 = getGitBeaker({
|
|
1816
|
+
url: options.repoUrl,
|
|
1817
|
+
gitlabAuthToken: options.accessToken
|
|
1818
|
+
});
|
|
1709
1819
|
const res = await api2.MergeRequests.create(
|
|
1710
1820
|
projectPath,
|
|
1711
1821
|
options.sourceBranchName,
|
|
@@ -1718,7 +1828,10 @@ async function createMergeRequest(options) {
|
|
|
1718
1828
|
return res.iid;
|
|
1719
1829
|
}
|
|
1720
1830
|
async function getGitlabRepoDefaultBranch(repoUrl, options) {
|
|
1721
|
-
const api2 = getGitBeaker({
|
|
1831
|
+
const api2 = getGitBeaker({
|
|
1832
|
+
url: repoUrl,
|
|
1833
|
+
gitlabAuthToken: options?.gitlabAuthToken
|
|
1834
|
+
});
|
|
1722
1835
|
const { projectPath } = parseGitlabOwnerAndRepo(repoUrl);
|
|
1723
1836
|
const project = await api2.Projects.show(projectPath);
|
|
1724
1837
|
if (!project.default_branch) {
|
|
@@ -1728,7 +1841,10 @@ async function getGitlabRepoDefaultBranch(repoUrl, options) {
|
|
|
1728
1841
|
}
|
|
1729
1842
|
async function getGitlabReferenceData({ ref, gitlabUrl }, options) {
|
|
1730
1843
|
const { projectPath } = parseGitlabOwnerAndRepo(gitlabUrl);
|
|
1731
|
-
const api2 = getGitBeaker({
|
|
1844
|
+
const api2 = getGitBeaker({
|
|
1845
|
+
url: gitlabUrl,
|
|
1846
|
+
gitlabAuthToken: options?.gitlabAuthToken
|
|
1847
|
+
});
|
|
1732
1848
|
const results = await Promise.allSettled([
|
|
1733
1849
|
(async () => {
|
|
1734
1850
|
const res = await api2.Branches.show(projectPath, ref);
|
|
@@ -1769,8 +1885,8 @@ async function getGitlabReferenceData({ ref, gitlabUrl }, options) {
|
|
|
1769
1885
|
}
|
|
1770
1886
|
function parseGitlabOwnerAndRepo(gitlabUrl) {
|
|
1771
1887
|
gitlabUrl = removeTrailingSlash2(gitlabUrl);
|
|
1772
|
-
const parsingResult = parseScmURL(gitlabUrl);
|
|
1773
|
-
if (!parsingResult || parsingResult.
|
|
1888
|
+
const parsingResult = parseScmURL(gitlabUrl, "GitLab" /* GitLab */);
|
|
1889
|
+
if (!parsingResult || !parsingResult.repoName) {
|
|
1774
1890
|
throw new InvalidUrlPatternError(`invalid gitlab repo Url ${gitlabUrl}`);
|
|
1775
1891
|
}
|
|
1776
1892
|
const { organization, repoName, projectPath } = parsingResult;
|
|
@@ -1778,7 +1894,10 @@ function parseGitlabOwnerAndRepo(gitlabUrl) {
|
|
|
1778
1894
|
}
|
|
1779
1895
|
async function getGitlabBlameRanges({ ref, gitlabUrl, path: path9 }, options) {
|
|
1780
1896
|
const { projectPath } = parseGitlabOwnerAndRepo(gitlabUrl);
|
|
1781
|
-
const api2 = getGitBeaker({
|
|
1897
|
+
const api2 = getGitBeaker({
|
|
1898
|
+
url: gitlabUrl,
|
|
1899
|
+
gitlabAuthToken: options?.gitlabAuthToken
|
|
1900
|
+
});
|
|
1782
1901
|
const resp = await api2.RepositoryFiles.allFileBlames(projectPath, path9, ref);
|
|
1783
1902
|
let lineNumber = 1;
|
|
1784
1903
|
return resp.filter((range) => range.lines).map((range) => {
|
|
@@ -1801,6 +1920,24 @@ var GitlabAuthResultZ = z4.object({
|
|
|
1801
1920
|
token_type: z4.string(),
|
|
1802
1921
|
refresh_token: z4.string()
|
|
1803
1922
|
});
|
|
1923
|
+
function initGitlabFetchMock() {
|
|
1924
|
+
const globalFetch = global.fetch;
|
|
1925
|
+
function myFetch(input, init) {
|
|
1926
|
+
const stack = new Error().stack;
|
|
1927
|
+
const parts = stack?.split("at ");
|
|
1928
|
+
if (parts?.length && parts?.length >= 3 && parts[2]?.startsWith("defaultRequestHandler (") && parts[2]?.includes("/@gitbeaker/rest/dist/index.js") && (/^https?:\/\/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\//i.test(
|
|
1929
|
+
input?.url
|
|
1930
|
+
) || "GITLAB_INTERNAL_DEV_HOST" in process.env && process.env["GITLAB_INTERNAL_DEV_HOST"] && input?.url?.startsWith(process.env["GITLAB_INTERNAL_DEV_HOST"]))) {
|
|
1931
|
+
const dispatcher = new ProxyAgent(
|
|
1932
|
+
process.env["GIT_PROXY_HOST"] || "http://tinyproxy:8888"
|
|
1933
|
+
);
|
|
1934
|
+
return globalFetch(input, { dispatcher });
|
|
1935
|
+
}
|
|
1936
|
+
return globalFetch(input, init);
|
|
1937
|
+
}
|
|
1938
|
+
global.fetch = myFetch;
|
|
1939
|
+
}
|
|
1940
|
+
initGitlabFetchMock();
|
|
1804
1941
|
|
|
1805
1942
|
// src/features/analysis/scm/scmSubmit/index.ts
|
|
1806
1943
|
import fs from "node:fs/promises";
|
|
@@ -1904,7 +2041,7 @@ function getCloudScmLibTypeFromUrl(url) {
|
|
|
1904
2041
|
return void 0;
|
|
1905
2042
|
}
|
|
1906
2043
|
const urlObject = new URL(url);
|
|
1907
|
-
const hostname = urlObject.hostname;
|
|
2044
|
+
const hostname = urlObject.hostname.toLowerCase();
|
|
1908
2045
|
if (hostname === "gitlab.com") {
|
|
1909
2046
|
return "GITLAB" /* GITLAB */;
|
|
1910
2047
|
}
|
|
@@ -1952,7 +2089,7 @@ function getScmConfig({
|
|
|
1952
2089
|
//if we the user does an ADO oauth flow then the token is saved for dev.azure.com but
|
|
1953
2090
|
//sometimes the user uses the url dev.azure.com and sometimes the url visualstudio.com
|
|
1954
2091
|
//so we need to check both
|
|
1955
|
-
(urlObject.hostname === configUrl.hostname || urlObject.hostname.endsWith(".visualstudio.com") && configUrl.hostname === "dev.azure.com") && urlObject.protocol === configUrl.protocol && urlObject.port === configUrl.port
|
|
2092
|
+
(urlObject.hostname.toLowerCase() === configUrl.hostname.toLowerCase() || urlObject.hostname.toLowerCase().endsWith(".visualstudio.com") && configUrl.hostname.toLowerCase() === "dev.azure.com") && urlObject.protocol === configUrl.protocol && urlObject.port === configUrl.port
|
|
1956
2093
|
);
|
|
1957
2094
|
});
|
|
1958
2095
|
const scmOrgConfig = filteredScmConfigs.find((scm) => scm.orgId && scm.token);
|
|
@@ -2285,6 +2422,15 @@ var AdoSCMLib = class extends SCMLib {
|
|
|
2285
2422
|
getPr() {
|
|
2286
2423
|
throw new Error("Method not implemented.");
|
|
2287
2424
|
}
|
|
2425
|
+
postGeneralPrComment() {
|
|
2426
|
+
throw new Error("Method not implemented.");
|
|
2427
|
+
}
|
|
2428
|
+
getGeneralPrComments() {
|
|
2429
|
+
throw new Error("Method not implemented.");
|
|
2430
|
+
}
|
|
2431
|
+
deleteGeneralPrComment() {
|
|
2432
|
+
throw new Error("Method not implemented.");
|
|
2433
|
+
}
|
|
2288
2434
|
};
|
|
2289
2435
|
var GitlabSCMLib = class extends SCMLib {
|
|
2290
2436
|
async createSubmitRequest(targetBranchName, sourceBranchName, title, body) {
|
|
@@ -2331,7 +2477,7 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
2331
2477
|
console.error("no access token");
|
|
2332
2478
|
throw new Error("no access token");
|
|
2333
2479
|
}
|
|
2334
|
-
return getGitlabRepoList(this.accessToken);
|
|
2480
|
+
return getGitlabRepoList(this.url, this.accessToken);
|
|
2335
2481
|
}
|
|
2336
2482
|
async getBranchList() {
|
|
2337
2483
|
if (!this.accessToken || !this.url) {
|
|
@@ -2394,7 +2540,7 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
2394
2540
|
console.error("no access token");
|
|
2395
2541
|
throw new Error("no access token");
|
|
2396
2542
|
}
|
|
2397
|
-
return getGitlabUsername(this.accessToken);
|
|
2543
|
+
return getGitlabUsername(this.url, this.accessToken);
|
|
2398
2544
|
}
|
|
2399
2545
|
async getSubmitRequestStatus(scmSubmitRequestId) {
|
|
2400
2546
|
if (!this.accessToken || !this.url) {
|
|
@@ -2425,6 +2571,7 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
2425
2571
|
return await getGitlabBlameRanges(
|
|
2426
2572
|
{ ref, path: path9, gitlabUrl: this.url },
|
|
2427
2573
|
{
|
|
2574
|
+
url: this.url,
|
|
2428
2575
|
gitlabAuthToken: this.accessToken
|
|
2429
2576
|
}
|
|
2430
2577
|
);
|
|
@@ -2437,6 +2584,7 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
2437
2584
|
return await getGitlabReferenceData(
|
|
2438
2585
|
{ ref, gitlabUrl: this.url },
|
|
2439
2586
|
{
|
|
2587
|
+
url: this.url,
|
|
2440
2588
|
gitlabAuthToken: this.accessToken
|
|
2441
2589
|
}
|
|
2442
2590
|
);
|
|
@@ -2447,6 +2595,7 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
2447
2595
|
throw new Error("no url");
|
|
2448
2596
|
}
|
|
2449
2597
|
return await getGitlabRepoDefaultBranch(this.url, {
|
|
2598
|
+
url: this.url,
|
|
2450
2599
|
gitlabAuthToken: this.accessToken
|
|
2451
2600
|
});
|
|
2452
2601
|
}
|
|
@@ -2459,6 +2608,15 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
2459
2608
|
getPr() {
|
|
2460
2609
|
throw new Error("Method not implemented.");
|
|
2461
2610
|
}
|
|
2611
|
+
postGeneralPrComment() {
|
|
2612
|
+
throw new Error("Method not implemented.");
|
|
2613
|
+
}
|
|
2614
|
+
getGeneralPrComments() {
|
|
2615
|
+
throw new Error("Method not implemented.");
|
|
2616
|
+
}
|
|
2617
|
+
deleteGeneralPrComment() {
|
|
2618
|
+
throw new Error("Method not implemented.");
|
|
2619
|
+
}
|
|
2462
2620
|
};
|
|
2463
2621
|
var GithubSCMLib = class extends SCMLib {
|
|
2464
2622
|
// we don't always need a url, what's important is that we have an access token
|
|
@@ -2727,6 +2885,48 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
2727
2885
|
pull_number: prNumber
|
|
2728
2886
|
});
|
|
2729
2887
|
}
|
|
2888
|
+
async postGeneralPrComment(params, auth) {
|
|
2889
|
+
const { prNumber, body } = params;
|
|
2890
|
+
if (!this.url) {
|
|
2891
|
+
console.error("no url");
|
|
2892
|
+
throw new Error("no url");
|
|
2893
|
+
}
|
|
2894
|
+
const oktoKit = auth ? new Octokit2({ auth: auth.authToken }) : this.oktokit;
|
|
2895
|
+
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
2896
|
+
return await postGeneralPrComment(oktoKit, {
|
|
2897
|
+
issue_number: prNumber,
|
|
2898
|
+
owner,
|
|
2899
|
+
repo,
|
|
2900
|
+
body
|
|
2901
|
+
});
|
|
2902
|
+
}
|
|
2903
|
+
async getGeneralPrComments(params, auth) {
|
|
2904
|
+
const { prNumber } = params;
|
|
2905
|
+
if (!this.url) {
|
|
2906
|
+
console.error("no url");
|
|
2907
|
+
throw new Error("no url");
|
|
2908
|
+
}
|
|
2909
|
+
const oktoKit = auth ? new Octokit2({ auth: auth.authToken }) : this.oktokit;
|
|
2910
|
+
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
2911
|
+
return await getGeneralPrComments(oktoKit, {
|
|
2912
|
+
issue_number: prNumber,
|
|
2913
|
+
owner,
|
|
2914
|
+
repo
|
|
2915
|
+
});
|
|
2916
|
+
}
|
|
2917
|
+
async deleteGeneralPrComment({ commentId }, auth) {
|
|
2918
|
+
if (!this.url) {
|
|
2919
|
+
console.error("no url");
|
|
2920
|
+
throw new Error("no url");
|
|
2921
|
+
}
|
|
2922
|
+
const oktoKit = auth ? new Octokit2({ auth: auth.authToken }) : this.oktokit;
|
|
2923
|
+
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
2924
|
+
return deleteGeneralPrComment(oktoKit, {
|
|
2925
|
+
owner,
|
|
2926
|
+
repo,
|
|
2927
|
+
comment_id: commentId
|
|
2928
|
+
});
|
|
2929
|
+
}
|
|
2730
2930
|
};
|
|
2731
2931
|
var StubSCMLib = class extends SCMLib {
|
|
2732
2932
|
async createSubmitRequest(_targetBranchName, _sourceBranchName, _title, _body) {
|
|
@@ -2813,6 +3013,15 @@ var StubSCMLib = class extends SCMLib {
|
|
|
2813
3013
|
console.error("getPr() not implemented");
|
|
2814
3014
|
throw new Error("getPr() not implemented");
|
|
2815
3015
|
}
|
|
3016
|
+
postGeneralPrComment() {
|
|
3017
|
+
throw new Error("Method not implemented.");
|
|
3018
|
+
}
|
|
3019
|
+
getGeneralPrComments() {
|
|
3020
|
+
throw new Error("Method not implemented.");
|
|
3021
|
+
}
|
|
3022
|
+
deleteGeneralPrComment() {
|
|
3023
|
+
throw new Error("Method not implemented.");
|
|
3024
|
+
}
|
|
2816
3025
|
};
|
|
2817
3026
|
|
|
2818
3027
|
// src/features/analysis/scm/ado.ts
|
|
@@ -3071,7 +3280,9 @@ function getAdoDownloadUrl({
|
|
|
3071
3280
|
branch
|
|
3072
3281
|
}) {
|
|
3073
3282
|
const { owner, repo, projectName } = parseAdoOwnerAndRepo(repoUrl);
|
|
3074
|
-
|
|
3283
|
+
const url = new URL(repoUrl);
|
|
3284
|
+
const origin = url.origin.toLowerCase().endsWith(".visualstudio.com") ? "https://dev.azure.com" : url.origin.toLowerCase();
|
|
3285
|
+
return `${origin}/${owner}/${projectName}/_apis/git/repositories/${repo}/items/items?path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
|
|
3075
3286
|
}
|
|
3076
3287
|
async function getAdoBranchList({
|
|
3077
3288
|
accessToken,
|
|
@@ -3241,7 +3452,7 @@ async function getAdoReferenceData({
|
|
|
3241
3452
|
}
|
|
3242
3453
|
function parseAdoOwnerAndRepo(adoUrl) {
|
|
3243
3454
|
adoUrl = removeTrailingSlash3(adoUrl);
|
|
3244
|
-
const parsingResult = parseScmURL(adoUrl);
|
|
3455
|
+
const parsingResult = parseScmURL(adoUrl, "Ado" /* Ado */);
|
|
3245
3456
|
if (!parsingResult || parsingResult.hostname !== "dev.azure.com" && !parsingResult.hostname.endsWith(".visualstudio.com")) {
|
|
3246
3457
|
throw new InvalidUrlPatternError(`invalid ADO repo URL: ${adoUrl}`);
|
|
3247
3458
|
}
|
|
@@ -3264,7 +3475,7 @@ var AdoAuthResultZ = z8.object({
|
|
|
3264
3475
|
});
|
|
3265
3476
|
|
|
3266
3477
|
// src/features/analysis/scm/constants.ts
|
|
3267
|
-
var MOBB_ICON_IMG = "https://
|
|
3478
|
+
var MOBB_ICON_IMG = "https://app.mobb.ai/gh-action/Logo_Rounded_Icon.svg";
|
|
3268
3479
|
var COMMIT_FIX_SVG = `https://app.mobb.ai/gh-action/commit-button.svg`;
|
|
3269
3480
|
|
|
3270
3481
|
// src/features/analysis/scm/utils/get_issue_type.ts
|
|
@@ -3379,6 +3590,13 @@ function getCommitUrl(params) {
|
|
|
3379
3590
|
})}/commit?${searchParams.toString()}`;
|
|
3380
3591
|
}
|
|
3381
3592
|
|
|
3593
|
+
// src/features/analysis/utils/by_key.ts
|
|
3594
|
+
function keyBy(array, keyBy2) {
|
|
3595
|
+
return array.reduce((acc, item) => {
|
|
3596
|
+
return { ...acc, [item[keyBy2]]: item };
|
|
3597
|
+
}, {});
|
|
3598
|
+
}
|
|
3599
|
+
|
|
3382
3600
|
// src/features/analysis/utils/calculate_ranges.ts
|
|
3383
3601
|
function calculateRanges(integers) {
|
|
3384
3602
|
if (integers.length === 0) {
|
|
@@ -3406,7 +3624,10 @@ function calculateRanges(integers) {
|
|
|
3406
3624
|
|
|
3407
3625
|
// src/features/analysis/handle_finished_analysis.ts
|
|
3408
3626
|
var debug4 = Debug4("mobbdev:handle-finished-analysis");
|
|
3627
|
+
var contactUsMarkdown = `For specific requests [contact us](https://mobb.ai/contact) and we'll do the most to answer your need quickly.`;
|
|
3409
3628
|
var commitFixButton = (commitUrl) => `<a href="${commitUrl}"><img src=${COMMIT_FIX_SVG}></a>`;
|
|
3629
|
+
var MobbIconMarkdown = ``;
|
|
3630
|
+
var noVulnerabilitiesFoundTitle = `# ${MobbIconMarkdown} No security issues were found \u2705`;
|
|
3410
3631
|
function scannerToFriendlyString(scanner) {
|
|
3411
3632
|
switch (scanner) {
|
|
3412
3633
|
case "checkmarx":
|
|
@@ -3419,7 +3640,7 @@ function scannerToFriendlyString(scanner) {
|
|
|
3419
3640
|
return "Snyk";
|
|
3420
3641
|
}
|
|
3421
3642
|
}
|
|
3422
|
-
async function
|
|
3643
|
+
async function getRelevantVulenrabilitiesFromDiff(params) {
|
|
3423
3644
|
const { gqlClient, diff, vulnerabilityReportId } = params;
|
|
3424
3645
|
const parsedDiff = parseDiff(diff);
|
|
3425
3646
|
const fileHunks = parsedDiff.map((file) => {
|
|
@@ -3442,13 +3663,45 @@ async function getFixesFromDiff(params) {
|
|
|
3442
3663
|
vulnerabilityReportId
|
|
3443
3664
|
});
|
|
3444
3665
|
}
|
|
3666
|
+
async function getFixesData(params) {
|
|
3667
|
+
const { gqlClient, fixesId } = params;
|
|
3668
|
+
const { fixes } = await gqlClient.getFixes(fixesId);
|
|
3669
|
+
return keyBy(fixes, "id");
|
|
3670
|
+
}
|
|
3671
|
+
function buildAnalysisSummaryComment(params) {
|
|
3672
|
+
const { prVulenrabilities: fixesFromDiff, fixesById } = params;
|
|
3673
|
+
const { vulnerabilityReportIssueCodeNodes, fixablePrVuls } = fixesFromDiff;
|
|
3674
|
+
const title = `# ${MobbIconMarkdown} ${fixablePrVuls} ${fixablePrVuls === 1 ? "fix is" : "fixes are"} ready to be committed`;
|
|
3675
|
+
const summary = Object.entries(
|
|
3676
|
+
// count every issue type
|
|
3677
|
+
vulnerabilityReportIssueCodeNodes.reduce(
|
|
3678
|
+
(result, vulnerabilityReportIssueCodeNode) => {
|
|
3679
|
+
const { vulnerabilityReportIssue } = vulnerabilityReportIssueCodeNode;
|
|
3680
|
+
const fix = fixesById[vulnerabilityReportIssue.fixId];
|
|
3681
|
+
if (!fix) {
|
|
3682
|
+
throw new Error(`fix ${vulnerabilityReportIssue.fixId} not found`);
|
|
3683
|
+
}
|
|
3684
|
+
const issueType = getIssueType(fix.issueType);
|
|
3685
|
+
const vulnerabilityReportIssueCount = (result[issueType] || 0) + 1;
|
|
3686
|
+
return {
|
|
3687
|
+
...result,
|
|
3688
|
+
[issueType]: vulnerabilityReportIssueCount
|
|
3689
|
+
};
|
|
3690
|
+
},
|
|
3691
|
+
{}
|
|
3692
|
+
)
|
|
3693
|
+
).map(([issueType, issueTypeCount]) => `**${issueType}** - ${issueTypeCount}`);
|
|
3694
|
+
return `${title}
|
|
3695
|
+
${summary.join("\n")}`;
|
|
3696
|
+
}
|
|
3445
3697
|
async function handleFinishedAnalysis({
|
|
3446
3698
|
analysisId,
|
|
3447
3699
|
scm: _scm,
|
|
3448
3700
|
gqlClient,
|
|
3449
|
-
|
|
3701
|
+
githubActionToken,
|
|
3450
3702
|
scanner
|
|
3451
3703
|
}) {
|
|
3704
|
+
const githubActionOctokit = new Octokit3({ auth: githubActionToken });
|
|
3452
3705
|
if (_scm instanceof GithubSCMLib === false) {
|
|
3453
3706
|
return;
|
|
3454
3707
|
}
|
|
@@ -3462,17 +3715,59 @@ async function handleFinishedAnalysis({
|
|
|
3462
3715
|
} = getAnalysis.analysis;
|
|
3463
3716
|
const { commitSha, pullRequest } = getAnalysis.analysis.repo;
|
|
3464
3717
|
const diff = await scm.getPrDiff({ pull_number: pullRequest });
|
|
3465
|
-
const
|
|
3718
|
+
const prVulenrabilities = await getRelevantVulenrabilitiesFromDiff({
|
|
3466
3719
|
diff,
|
|
3467
3720
|
gqlClient,
|
|
3468
3721
|
vulnerabilityReportId: getAnalysis.analysis.vulnerabilityReportId
|
|
3469
3722
|
});
|
|
3470
|
-
const
|
|
3471
|
-
|
|
3472
|
-
|
|
3723
|
+
const { vulnerabilityReportIssueCodeNodes } = prVulenrabilities;
|
|
3724
|
+
const fixesId = vulnerabilityReportIssueCodeNodes.map(
|
|
3725
|
+
({ vulnerabilityReportIssue: { fixId } }) => fixId
|
|
3473
3726
|
);
|
|
3474
|
-
await
|
|
3475
|
-
|
|
3727
|
+
const fixesById = await getFixesData({ fixesId, gqlClient });
|
|
3728
|
+
const [comments, generalPrComments] = await Promise.all([
|
|
3729
|
+
scm.getPrComments({ pull_number: pullRequest }, githubActionOctokit),
|
|
3730
|
+
scm.getGeneralPrComments(
|
|
3731
|
+
{ prNumber: pullRequest },
|
|
3732
|
+
{ authToken: githubActionToken }
|
|
3733
|
+
)
|
|
3734
|
+
]);
|
|
3735
|
+
async function postAnalysisSummary() {
|
|
3736
|
+
if (Object.values(fixesById).length === 0) {
|
|
3737
|
+
return;
|
|
3738
|
+
}
|
|
3739
|
+
const analysisSummaryComment = buildAnalysisSummaryComment({
|
|
3740
|
+
fixesById,
|
|
3741
|
+
prVulenrabilities
|
|
3742
|
+
});
|
|
3743
|
+
await scm.postGeneralPrComment(
|
|
3744
|
+
{
|
|
3745
|
+
body: analysisSummaryComment,
|
|
3746
|
+
prNumber: pullRequest
|
|
3747
|
+
},
|
|
3748
|
+
{ authToken: githubActionToken }
|
|
3749
|
+
);
|
|
3750
|
+
}
|
|
3751
|
+
function deleteAllPreviousGeneralPrComments() {
|
|
3752
|
+
return generalPrComments.data.filter((comment) => {
|
|
3753
|
+
if (!comment.body) {
|
|
3754
|
+
return false;
|
|
3755
|
+
}
|
|
3756
|
+
return comment.body.includes(MOBB_ICON_IMG);
|
|
3757
|
+
}).map((comment) => {
|
|
3758
|
+
try {
|
|
3759
|
+
return scm.deleteGeneralPrComment(
|
|
3760
|
+
{ commentId: comment.id },
|
|
3761
|
+
{ authToken: githubActionToken }
|
|
3762
|
+
);
|
|
3763
|
+
} catch (e) {
|
|
3764
|
+
debug4("delete comment failed %s", e);
|
|
3765
|
+
return Promise.resolve();
|
|
3766
|
+
}
|
|
3767
|
+
});
|
|
3768
|
+
}
|
|
3769
|
+
function deleteAllPreviousComments() {
|
|
3770
|
+
return comments.data.filter((comment) => {
|
|
3476
3771
|
return comment.body.includes(MOBB_ICON_IMG);
|
|
3477
3772
|
}).map((comment) => {
|
|
3478
3773
|
try {
|
|
@@ -3484,70 +3779,154 @@ async function handleFinishedAnalysis({
|
|
|
3484
3779
|
debug4("delete comment failed %s", e);
|
|
3485
3780
|
return Promise.resolve();
|
|
3486
3781
|
}
|
|
3487
|
-
})
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3782
|
+
});
|
|
3783
|
+
}
|
|
3784
|
+
await Promise.all([
|
|
3785
|
+
...deleteAllPreviousComments(),
|
|
3786
|
+
...deleteAllPreviousGeneralPrComments()
|
|
3787
|
+
]);
|
|
3788
|
+
async function postFixComment(vulnerabilityReportIssueCodeNode) {
|
|
3789
|
+
const {
|
|
3790
|
+
path: path9,
|
|
3791
|
+
startLine,
|
|
3792
|
+
vulnerabilityReportIssue: { fixId }
|
|
3793
|
+
} = vulnerabilityReportIssueCodeNode;
|
|
3794
|
+
const fix = fixesById[fixId];
|
|
3795
|
+
if (!fix) {
|
|
3796
|
+
throw new Error(`fix ${fixId} not found`);
|
|
3797
|
+
}
|
|
3798
|
+
const {
|
|
3799
|
+
patchAndQuestions: { patch }
|
|
3800
|
+
} = fix;
|
|
3801
|
+
const commentRes = await scm.postPrComment(
|
|
3802
|
+
{
|
|
3803
|
+
body: "empty",
|
|
3804
|
+
pull_number: pullRequest,
|
|
3805
|
+
commit_id: commitSha,
|
|
3806
|
+
path: path9,
|
|
3807
|
+
line: startLine
|
|
3808
|
+
},
|
|
3809
|
+
githubActionOctokit
|
|
3810
|
+
);
|
|
3811
|
+
const commentId = commentRes.data.id;
|
|
3812
|
+
const commitUrl = getCommitUrl({
|
|
3813
|
+
appBaseUrl: WEB_APP_URL,
|
|
3814
|
+
fixId,
|
|
3815
|
+
projectId,
|
|
3816
|
+
analysisId,
|
|
3817
|
+
organizationId,
|
|
3818
|
+
redirectUrl: commentRes.data.html_url,
|
|
3819
|
+
commentId
|
|
3820
|
+
});
|
|
3821
|
+
const fixUrl = getFixUrlWithRedirect({
|
|
3822
|
+
appBaseUrl: WEB_APP_URL,
|
|
3823
|
+
fixId,
|
|
3824
|
+
projectId,
|
|
3825
|
+
analysisId,
|
|
3826
|
+
organizationId,
|
|
3827
|
+
redirectUrl: commentRes.data.html_url,
|
|
3828
|
+
commentId
|
|
3829
|
+
});
|
|
3830
|
+
const scanerString = scannerToFriendlyString(scanner);
|
|
3831
|
+
const issueType = getIssueType(fix.issueType);
|
|
3832
|
+
const title = `# ${MobbIconMarkdown} ${issueType} fix is ready`;
|
|
3833
|
+
const subTitle = `### Apply the following code change to fix ${issueType} issue detected by **${scanerString}**:`;
|
|
3834
|
+
const diff2 = `\`\`\`diff
|
|
3532
3835
|
${patch}
|
|
3533
3836
|
\`\`\``;
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3837
|
+
const fixPageLink = `[Learn more and fine tune the fix](${fixUrl})`;
|
|
3838
|
+
await scm.updatePrComment(
|
|
3839
|
+
{
|
|
3840
|
+
body: `${title}
|
|
3538
3841
|
${subTitle}
|
|
3539
3842
|
${diff2}
|
|
3540
3843
|
${commitFixButton(
|
|
3541
|
-
|
|
3542
|
-
|
|
3844
|
+
commitUrl
|
|
3845
|
+
)}
|
|
3543
3846
|
${fixPageLink}`,
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3847
|
+
comment_id: commentId
|
|
3848
|
+
},
|
|
3849
|
+
githubActionOctokit
|
|
3850
|
+
);
|
|
3851
|
+
}
|
|
3852
|
+
async function postAnalysisInsightComment() {
|
|
3853
|
+
const scanerString = scannerToFriendlyString(scanner);
|
|
3854
|
+
const {
|
|
3855
|
+
totalPrVulnerabilities,
|
|
3856
|
+
vulnerabilitiesOutsidePr,
|
|
3857
|
+
fixablePrVuls,
|
|
3858
|
+
nonFixablePrVuls
|
|
3859
|
+
} = prVulenrabilities;
|
|
3860
|
+
debug4({
|
|
3861
|
+
fixablePrVuls,
|
|
3862
|
+
nonFixablePrVuls,
|
|
3863
|
+
vulnerabilitiesOutsidePr,
|
|
3864
|
+
totalPrVulnerabilities
|
|
3865
|
+
});
|
|
3866
|
+
if (totalPrVulnerabilities === 0 && vulnerabilitiesOutsidePr === 0) {
|
|
3867
|
+
const body = `Awesome! No vulnerabilities were found by **${scanerString}**`;
|
|
3868
|
+
const noVulsFoundComment = `${noVulnerabilitiesFoundTitle}
|
|
3869
|
+
${body}`;
|
|
3870
|
+
await scm.postGeneralPrComment(
|
|
3871
|
+
{
|
|
3872
|
+
body: noVulsFoundComment,
|
|
3873
|
+
prNumber: pullRequest
|
|
3874
|
+
},
|
|
3875
|
+
{ authToken: githubActionToken }
|
|
3876
|
+
);
|
|
3877
|
+
return;
|
|
3878
|
+
}
|
|
3879
|
+
if (totalPrVulnerabilities === 0 && vulnerabilitiesOutsidePr > 0) {
|
|
3880
|
+
const body = `Awesome! No vulnerabilities were found by **${scanerString}** in the changes made as part of this PR.`;
|
|
3881
|
+
const body2 = `Please notice there are issues in this repo that are unrelated to this PR.`;
|
|
3882
|
+
const noVulsFoundComment = `${noVulnerabilitiesFoundTitle}
|
|
3883
|
+
${body}
|
|
3884
|
+
${body2}`;
|
|
3885
|
+
await scm.postGeneralPrComment(
|
|
3886
|
+
{
|
|
3887
|
+
body: noVulsFoundComment,
|
|
3888
|
+
prNumber: pullRequest
|
|
3889
|
+
},
|
|
3890
|
+
{ authToken: githubActionToken }
|
|
3891
|
+
);
|
|
3892
|
+
return;
|
|
3893
|
+
}
|
|
3894
|
+
if (fixablePrVuls === 0 && nonFixablePrVuls > 0) {
|
|
3895
|
+
const title = `# ${MobbIconMarkdown} We couldn't fix the issues detected by **${scanerString}**`;
|
|
3896
|
+
const body = `Mobb Fixer gets better and better every day, but unfortunately your current issues aren't supported yet.`;
|
|
3897
|
+
const noFixableVulsComment = `${title}
|
|
3898
|
+
${body}
|
|
3899
|
+
${contactUsMarkdown}`;
|
|
3900
|
+
await scm.postGeneralPrComment(
|
|
3901
|
+
{
|
|
3902
|
+
body: noFixableVulsComment,
|
|
3903
|
+
prNumber: pullRequest
|
|
3904
|
+
},
|
|
3905
|
+
{ authToken: githubActionToken }
|
|
3906
|
+
);
|
|
3907
|
+
return;
|
|
3908
|
+
}
|
|
3909
|
+
if (fixablePrVuls < nonFixablePrVuls && fixablePrVuls > 0) {
|
|
3910
|
+
const title = `# ${MobbIconMarkdown} We couldn't fix some of the issues detected by **${scanerString}**`;
|
|
3911
|
+
const body = "Mobb Fixer gets better and better every day, but unfortunately your current issues aren't supported yet.";
|
|
3912
|
+
const fixableVulsComment = `${title}
|
|
3913
|
+
${body}
|
|
3914
|
+
${contactUsMarkdown}`;
|
|
3915
|
+
await scm.postGeneralPrComment(
|
|
3916
|
+
{
|
|
3917
|
+
body: fixableVulsComment,
|
|
3918
|
+
prNumber: pullRequest
|
|
3919
|
+
},
|
|
3920
|
+
{ authToken: githubActionToken }
|
|
3921
|
+
);
|
|
3922
|
+
return;
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
await Promise.all([
|
|
3926
|
+
...prVulenrabilities.vulnerabilityReportIssueCodeNodes.map(postFixComment),
|
|
3927
|
+
postAnalysisInsightComment(),
|
|
3928
|
+
postAnalysisSummary()
|
|
3929
|
+
]);
|
|
3551
3930
|
}
|
|
3552
3931
|
|
|
3553
3932
|
// src/features/analysis/pack.ts
|
|
@@ -4162,7 +4541,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
4162
4541
|
analysisId,
|
|
4163
4542
|
gqlClient,
|
|
4164
4543
|
scm,
|
|
4165
|
-
|
|
4544
|
+
githubActionToken: z10.string().parse(githubActionToken),
|
|
4166
4545
|
scanner: z10.nativeEnum(SCANNERS).parse(scanner)
|
|
4167
4546
|
})
|
|
4168
4547
|
);
|
|
@@ -4613,7 +4992,7 @@ Example:
|
|
|
4613
4992
|
}
|
|
4614
4993
|
var UrlZ = z11.string({
|
|
4615
4994
|
invalid_type_error: "is not a valid GitHub / GitLab / ADO URL"
|
|
4616
|
-
}).refine((data) => !!
|
|
4995
|
+
}).refine((data) => !!sanityRepoURL(data), {
|
|
4617
4996
|
message: "is not a valid GitHub / GitLab / ADO URL"
|
|
4618
4997
|
});
|
|
4619
4998
|
function validateRepoUrl(args) {
|
|
@@ -4778,10 +5157,10 @@ function validateAddScmTokenOptions(argv) {
|
|
|
4778
5157
|
);
|
|
4779
5158
|
}
|
|
4780
5159
|
const urlObj = new URL(argv.url);
|
|
4781
|
-
if (urlObj.hostname === "github.com" && !argv.username) {
|
|
5160
|
+
if (urlObj.hostname.toLowerCase() === "github.com" && !argv.username) {
|
|
4782
5161
|
throw new CliError("\nError: --username flag is required for GitHub");
|
|
4783
5162
|
}
|
|
4784
|
-
if ((urlObj.hostname === "dev.azure.com" || urlObj.hostname.endsWith(".visualstudio.com")) && !argv.organization) {
|
|
5163
|
+
if ((urlObj.hostname.toLowerCase() === "dev.azure.com" || urlObj.hostname.toLowerCase().endsWith(".visualstudio.com")) && !argv.organization) {
|
|
4785
5164
|
throw new CliError(
|
|
4786
5165
|
"\nError: --organization flag is required for Azure DevOps"
|
|
4787
5166
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mobbdev",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.86",
|
|
4
4
|
"description": "Automated secure code remediation tool",
|
|
5
5
|
"repository": "https://github.com/mobb-dev/bugsy",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"supports-color": "9.4.0",
|
|
59
59
|
"tar": "6.2.0",
|
|
60
60
|
"tmp": "0.2.1",
|
|
61
|
+
"undici": "6.7.0",
|
|
61
62
|
"uuid": "9.0.1",
|
|
62
63
|
"ws": "8.10.0",
|
|
63
64
|
"yargs": "17.7.2",
|